#include "cubeInstance.hh"
#include "GLShaderManager.hh"
#include "GLBufferManager.hh"
#include "cubeWorld.hh"

#include "oglHeaders.hh"

//Class CubeInstance
CubeInstance::CubeInstance(unsigned int id)
:_mID(id)
{
   ResetData();
}

CubeInstance::CubeInstance(unsigned int id, const float trans_mat[16], const float color[4])
:_mID(id)
 {
    SetData(trans_mat, color);
 }

CubeInstance::~CubeInstance(void)
{
   
}

void CubeInstance::ResetData(void)
{
   for(int i=0; i<16; i++)
   {
      _mData.mTransfMatrix[i] = 0.0f;
   }

   for(int i=0; i<4; i++)
   {
      _mData.mColor[i] = 0.0f;
   }
   
   _mData.mEnable = 0.0f;
}


void CubeInstance::SetData(const float trans_mat[16], const float color[4])
{
   for(int i=0; i<16; i++)
   {
      _mData.mTransfMatrix[i] = trans_mat[i];
   }

   for(int i=0; i<4; i++)
   {
      _mData.mColor[i] = color[i];
   }
   
   _mData.mEnable = 1.0f;
}

void CubeInstance::SetDataMatrix(float trans_mat[16])
{
   for(int i=0; i<16; i++)
   {
      _mData.mTransfMatrix[i] = trans_mat[i];
   }
}

void CubeInstance::SetDataColor(float color[4])
{
   for(int i=0; i<4; i++)
   {
      _mData.mColor[i] = color[i];
   }
}
void CubeInstance::SetDataColor(float r, float g, float b, float a)
{
   _mData.mColor[0] = r;
   _mData.mColor[1] = g;
   _mData.mColor[2] = b;
   _mData.mColor[3] = a;
}


void CubeInstance::GetDataMatrix(unsigned int id, float trans_mat[16])
{
   for(int i=0; i<16; i++)
   {
     trans_mat[i] = _mData.mTransfMatrix[i] ;
   }
   
}

void CubeInstance::GetDataColor(float color[4])
{
   for(int i=0; i<4; i++)
   {
     color[i] = _mData.mColor[i] ;
   }
}


//Class CubeInstanceManager

CubeInstanceManager* CubeInstanceManager::_mSingleton = NULL;

CubeInstanceManager::CubeInstanceManager(void)
 :_mCubeInstBufferPointer(NULL),
  _mCubeInstBufferDirty(true)
{
   
}

CubeInstanceManager::~CubeInstanceManager(void)
{
   
}

CubeInstanceManager* CubeInstanceManager::GetSingleton(void)
{
   if(!_mSingleton)
   {
      _mSingleton = new CubeInstanceManager;
   }

   return _mSingleton;
}

CubeInstance* CubeInstanceManager::GetCubeInstance(unsigned int id)
{
   std::vector<CubeInstance*>::iterator itr = _mCubeInstVector.begin();

   for(; itr!=_mCubeInstVector.end(); itr++)
   {
      if( id==(*itr)->GetID() ) return (*itr);
   }
   return NULL;
}

unsigned int CubeInstanceManager::GetCubeInstNums(void)
{
   return _mCubeInstVector.size();
}

CubeInstanceData* CubeInstanceManager::GetCubeInstPointer(void)
{
   if(!_mCubeInstBufferPointer || _mCubeInstBufferDirty) {
      unsigned int size = _mCubeInstVector.size();
      _mCubeInstBuffer.clear();
      for(unsigned int i=0; i<size; i++) {
	 _mCubeInstBuffer.push_back(*(_mCubeInstVector[i]->GetData()));
      }
      _mCubeInstBufferPointer = &_mCubeInstBuffer[0];
      _mCubeInstBufferDirty = false;
   }
   return _mCubeInstBufferPointer;
}

unsigned int CubeInstanceManager::GetCubeInstTotalByteSize(void)
{
   return _mCubeInstVector.size() * sizeof(CubeInstanceData);
}

bool CubeInstanceManager::PullCubeInstFrom(CubeWorld* world)
{
   if(!world) return false;

   float defColor[4] = {1.0f, 0.0f, 0.0f, 1.0f};
   float matrix[16];

   float cubeEdgeSize = world->GetCubeEdgeSize();
   
   glPushMatrix();
   glMatrixMode(GL_MODELVIEW);      
   glLoadIdentity();

   unsigned int zNums = world->GetCubeZNums();
   unsigned int yNums = world->GetCubeYNums();
   unsigned int xNums = world->GetCubeXNums();
   
   for(unsigned int z=0; z<zNums; z++)
   {
      glPushMatrix();
      for(unsigned int y=0; y<yNums; y++)
      {
	 glPushMatrix();
	 for(unsigned int x=0; x<xNums; x++)
	 {
	    glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
	    CubeInstance* cube = _CreateCubeInst(_mCubeInstVector.size(), matrix, defColor);
	    _mCubeInstVector.push_back(cube);
	    glTranslatef(cubeEdgeSize, 0, 0);
	 }
	 glPopMatrix();
	 glTranslatef(0, cubeEdgeSize, 0);
      }
      glPopMatrix();
      glTranslatef(0, 0, cubeEdgeSize);
   }
   
   glPopMatrix();

   return true;
}

bool CubeInstanceManager::PushCubeInstTo(const char* buf_name)
{
   GLBufferManager* gbManager = GLBufferManager::GetSingleton();
   if(gbManager)
   {
      unsigned int buf_id = gbManager->GetGLBuffer(buf_name);
      return PushCubeInstTo(buf_id);
   }

   return false;
}

bool CubeInstanceManager::PushCubeInstTo(unsigned int buf_id)
{
   GLShaderManager* gsManager = GLShaderManager::GetSingleton();
   if(gsManager)
   {
//      GLsizei stride = (GLsizei)GetCubeInstByteSize();
      GLsizei stride = sizeof(CubeInstanceData);

      if(gsManager->FillGLInstanceAttribBegin(buf_id)) {
	 
	 gsManager->FillGLInstanceAttrib("CI_TransfMat", InstAttr_Matrix, stride);
	 gsManager->FillGLInstanceAttrib( "CI_Color", InstAttr_Vector, stride);
	 gsManager->FillGLInstanceAttrib( "CI_Enable", InstAttr_Float, stride);

	 gsManager->FillGLInstanceAttribEnd();
	 return true;
      }
   }
   return false;
}

CubeInstance* CubeInstanceManager::_CreateCubeInst(unsigned int id, const float trans_mat[16], const float color[4])
{
   CubeInstance* inst = new CubeInstance(id, trans_mat, color);
   return inst;
}
